#include "coder.h"
#include <math.h>

#include <QDebug>

// Логарифм по основанию 2
inline double log2(double x){ return log(x) / log(2); }

Coder::Coder(QObject *p):
	QObject(p){}

void Coder::createTable(int kolMsg){
	N = kolMsg;

	Nu = ceil(log2(N));
	Nk = ceil(log2(Nu + 1 + ceil(log2(Nu + 1))));
	n = Nu + Nk;

	qDebug()<<"Nu"<<Nu<<"Nk"<<Nk<<"n"<<n;

	// Заполняю вспомагательную таблицу (состояющую из порядковых номеров строк в двоичном виде)
	QString tmp;
	// Пробегаю по строкам таблицы
	for (int i = 0; i < n; ++i)
	{
		// привожу к двоичному виду порядковые номера строк
		tmp = QString::number(i+1, 2);
		// забиваю нулями начало до необходимой длины
		table.insert(i, QString(Nk - tmp.size(), '0'));
		table[i].append(tmp);
	}
}

// Кодирование сообщения
QString Coder::coded(const QString & s){
	QString codedMessage = s;

	// Индексы
	int i, j, k, sum;

	// Заполняю вектор кодируемых сообщений (на месте контрольных разрядов вставлю 0) ///////
	for (i = 0; i < Nk; ++i)
		codedMessage.insert((int)pow(2, i)-1, '0');
	/////////////////////////////////////////////////////////////////////////////////////////

	// Заполняю контрольные разряды /////////////////////////////////////////////////////////
	k = 0;
	// Пробегаю по столбцам вспомагательной таблицы в обратном порядке
	for (j = Nk-1; j >= 0; --j)
	{
		sum = 0;

		// Пробегаю по строкам вспомагательной таблицы
		for (i = 0; i < n; ++i)
			// Нахожу сумму
			if (table[i].at(j) == '1')
				sum += codedMessage.mid(i, 1).toInt();

		codedMessage.replace((int)pow(2, k)-1, 1, QString::number(sum % 2));

		++k;
	}
	/////////////////////////////////////////////////////////////////////////////////////////

	// Вставляю бит чётности ////////////////////////////////////////////////////////////////
	sum = 0;

	for (i = 0; i < n; ++i)
		sum += codedMessage.mid(i, 1).toInt();

	codedMessage.append(QString::number(sum % 2));
	/////////////////////////////////////////////////////////////////////////////////////////

	return codedMessage;
}

// Декодирование сообщения
QString Coder::decoded(const QString & s){
	QString decodedMessage = s;

	// Индексы
	int i, j, k, sum = 0;

	// Проверяю чётность ////////////////////////////////////////////////////////////////////
	for (i = 0; i < decodedMessage.length(); ++i)
		sum += decodedMessage.mid(i, 1).toInt();

	// Если 1 - чётность нарушена, если 0 - нет
	int chot = sum % 2;
	/////////////////////////////////////////////////////////////////////////////////////////

	// Нахожу вектор ошибок /////////////////////////////////////////////////////////////////
	k = 0;
	// Пробегаю по столбцам вспомагательной таблицы в обратном порядке
	for (j = Nk-1; j >= 0; --j)
	{
		sum = 0;

		// Пробегаю по строкам вспомагательной таблицы
		for (i = 0; i < n; ++i)
			if (table[i].at(j) == '1')
				sum += decodedMessage.mid(i, 1).toInt();

		S.insert(k, sum % 2);

		++k;
	}
	/////////////////////////////////////////////////////////////////////////////////////////

	// Перегоняю вектор ошибок в строку для последующего сравнения с таблицей
	QString errVec;
	for (j = Nk-1; j >= 0; --j)
		errVec.append(QString::number(S[j]));

	bool flag = true; // Для различия отсутствия ошибок и двойной (true - нет ошибок)

	// Сравниваю ошибку с таблицей
	for (i = 0; i < n; ++i)
	{
		if (table[i] == errVec)
		{
			if (chot == 1)
			{
				// Исправляю одинарную ошибку
				if (decodedMessage[i] == '1')
					decodedMessage.replace(i, 1, '0');
				else
					decodedMessage.replace(i, 1, '1');

				decodedMessage.append(trUtf8(" Ошибка в бите %1.").arg(i+1));
			}
			else
				decodedMessage.append(trUtf8(" Ошибка в двух битах."));

			flag = false;
		}
	}

	// Ошибка в бите чётности, если вектор ошибок нулевой и нарушена чётность
	if (chot == 1 && flag)
		decodedMessage.append(trUtf8(" Ошибка во множестве бит."));
	// Ошибок нет, если вектор ошибок нулевой и не нарушена чётность
	if (chot == 0 && flag)
		decodedMessage.append(trUtf8(" Нет ошибок."));
	/////////////////////////////////////////////////////////////////////////////////////////

	// Удаляю контрольные разряды ///////////////////////////////////////////////////////////
	decodedMessage.remove(n, 1);
	for (i = Nk-1; i >= 0; --i)
		decodedMessage.remove((int)pow(2, i)-1, 1);
	/////////////////////////////////////////////////////////////////////////////////////////

	return decodedMessage;
}
